/**
 * 数据字典
 * @author luguoxiang
 * @date 2021-09-07
 * 开源项目：https://gitee.com/lgx1992/lg-soar 求star！请给我star！请帮我点个star！
 */
import { reactive, ref, watch } from 'vue';
import {
  createCache,
  distinct,
  forEach,
  formatStr,
  mergeLoad,
  request,
  toAsyncLoader,
  toStringArray,
  toTreeData,
} from '../utils';

/**
 * 数据字典
 */
export interface DictOptions {
  /**
   * 字典code
   */
  code: string;
}

/**
 * 选择器
 * @author luguoxiang
 * @date 2021-09-07
 * 开源项目：https://gitee.com/lgx1992/lg-soar 求star！请给我star！请帮我点个star！
 */
export interface BizSelectOptions {
  /**
   * 数据配置
   */
  data: Function | string | Object[];
  /**
   * 查询字段配置，默认第一个作为value,第二个作为label
   */
  fields?: string | string[];
  /**
   * 默认排序字段（优先级低于列配置）
   */
  orderBys?: string | string[];
  /**
   * 过滤条件
   */
  filters?: string | string[];
  /**
   * 值格式或字段，例：field 或 ${field1}-${field2}
   * 非必填
   */
  valueField?: string;
  /**
   * label格式，例：field 或 ${field1}（${field2}）
   * 非必填
   */
  labelField?: string;

  /**
   * 分页大小
   */
  pageSize?: number;
  /**
   * 数据处理器
   */
  handler?: Function;
  /**
   * 前面的选项
   */
  prepend?: Function | Object[] | Object;
  /**
   * 后面的选项
   */
  append?: Function | Object[] | Object;
}

export interface BizTreeOptions extends BizSelectOptions {
  /**
   * 父id字段，格式，例：field 或 ${field1}（${field2}）
   */
  pidField?: string;
}

/**
 * 获取数据
 * @param data
 */
function getDataArray(data) {
  if (typeof data === 'function') {
    data = data();
  }
  if (Array.isArray(data)) {
    return data;
  }
  return [data];
}

// 创建数据字典缓存
const dictCache = createCache('dict');

/**
 * 获取数据字典（自带缓存）
 * @param code
 * @param mode 响应数据结构
 */
export function getDict(code: string, mode?: 'list' | 'map' | 'text' | 'all') {
  const getter = dictCache.load(
    code,
    () => {
      // 使用合并读取，减少请求次数
      return mergeLoad({
        params: code,
        name: 'dict',
        loader: (codes: string[]) => request.get('/system/dict-item/map', { params: { codes: codes.join(',') } }),
        responseHandler: (res: any, params: any) => res[params],
      });
    },
    600,
  );

  switch (mode) {
    case 'all':
      return getter.then((res) => {
        const result = { list: res, map: {}, text: {} };
        for (const x of res) {
          result.text[x.value] = x.label;
          result.map[x.value] = x;
        }
        return result;
      });
    case 'map':
      return getter.then((res) => {
        const result = {};
        for (const x of res) {
          result[x.value] = x;
        }
        return result;
      });
    case 'text':
      return getter.then((res) => {
        const result = {};
        for (const x of res) {
          result[x.value] = x.label;
        }
        return result;
      });
    default:
      return getter;
  }
}

/**
 * 使用数据字典 list
 * @param config
 */
export function useDict(config: DictOptions) {
  const loading = ref(false);
  const options = ref([]);

  watch(
    () => config.code,
    (v) => {
      getDict(v)
        .then((res) => {
          options.value = res;
        })
        .finally(() => {
          loading.value = false;
        });
      loading.value = true;
    },
    { immediate: true },
  );

  return {
    loading,
    options,
  };
}

/**
 * 使用数据字典 map
 * @param config
 */
export function useDictMap(config: DictOptions) {
  const loading = ref(false);
  const options = ref({});

  watch(
    () => config.code,
    (v) => {
      getDict(v, 'map')
        .then((res) => {
          options.value = res;
        })
        .finally(() => {
          loading.value = false;
        });
      loading.value = true;
    },
    { immediate: true },
  );

  return {
    loading,
    options,
  };
}

/**
 * 使用数据字典 text
 * @param config
 */
export function useDictText(config: DictOptions) {
  const loading = ref(false);
  const options = ref({});

  watch(
    () => config.code,
    (v) => {
      getDict(v, 'text')
        .then((res) => {
          options.value = res;
        })
        .finally(() => {
          loading.value = false;
        });
      loading.value = true;
    },
    { immediate: true },
  );

  return {
    loading,
    options,
  };
}

/**
 * 使用数据字典（批量）
 * @param codes
 */
export function useDictTextBatch(codes: string[]) {
  let result = {};
  for (const code of codes) {
    result[code] = {};
  }
  result = reactive(result);
  for (const code of codes) {
    getDict(code, 'text').then((res) => {
      result[code] = res;
    });
  }
  return result;
}

/**
 * 使用业务选择数据
 * @param config
 */
export function useBizSelect(config: BizSelectOptions) {
  const loading = ref(false);
  const options = ref<any[]>([]);

  let configJson = null;
  function reload(params?: any) {
    if (configJson === JSON.stringify(config)) {
      return;
    }
    configJson = JSON.stringify(config);
    let fields = [];
    if (config.fields) {
      fields = fields.concat(toStringArray(config.fields));
    }
    if (config.valueField) {
      fields = fields.concat(formatStr.getPatternKeys(config.valueField));
    }
    if (config.labelField) {
      fields = fields.concat(formatStr.getPatternKeys(config.labelField));
    }
    if (fields.length) {
      fields = distinct(fields);
    } else {
      fields = ['id', 'name'];
    }
    const $soarQuery: any = {
      fields,
      orderBys: toStringArray(config.orderBys ?? []),
      filters: toStringArray(config.filters ?? [], ' && '),
      ...params,
    };
    // 发起请求
    toAsyncLoader(config.data)({ $soarQuery, pageNo: 1, pageSize: config.pageSize ?? 1000 })
      .then((res: any) => {
        res ??= [];
        res = Array.isArray(res) ? res : res.list;
        let list;
        if (typeof config.handler === 'function') {
          list = config.handler(res);
        } else {
          const valueField = config.valueField ?? fields[0];
          const labelField = config.labelField ?? fields[1] ?? valueField;
          list = res.map((x: any) => {
            return {
              value: formatStr(valueField, x),
              label: formatStr(labelField, x),
            };
          });
        }
        if (config.prepend) {
          list = getDataArray(config.prepend).concat(list);
        }
        if (config.append) {
          list = list.concat(getDataArray(config.append));
        }
        options.value = list;
      })
      .finally(() => {
        loading.value = false;
      });
    loading.value = true;
  }

  watch(
    () => config,
    () => reload(),
    { deep: true, immediate: true },
  );

  return {
    loading,
    options,
    reload,
  };
}

/**
 * 使用业务树数据
 * @param config
 */
export function useBizTree(config: BizTreeOptions) {
  const loading = ref(false);
  const options = ref<any[]>([]);

  let configJson = null;
  function reload(params?: any) {
    if (configJson === JSON.stringify(config)) {
      return;
    }
    configJson = JSON.stringify(config);
    let fields = [];
    if (config.fields) {
      fields = fields.concat(toStringArray(config.fields));
    }
    if (config.valueField) {
      fields = fields.concat(formatStr.getPatternKeys(config.valueField));
    }
    if (config.labelField) {
      fields = fields.concat(formatStr.getPatternKeys(config.labelField));
    }
    if (config.pidField) {
      fields = fields.concat(formatStr.getPatternKeys(config.pidField));
    }
    if (fields.length) {
      fields = distinct(fields);
    } else {
      fields = ['id', 'name', 'parentId'];
    }
    const $soarQuery: any = {
      fields,
      orderBys: toStringArray(config.orderBys ?? []),
      filters: toStringArray(config.filters ?? [], ' && '),
      ...params,
    };
    // 发起请求
    toAsyncLoader(config.data)({ $soarQuery, pageNo: 1, pageSize: config.pageSize ?? 1000 })
      .then((res: any) => {
        res ??= [];
        res = Array.isArray(res) ? res : res.list;
        let tree;
        if (typeof config.handler === 'function') {
          tree = config.handler(res);
        } else {
          const valueField = config.valueField ?? fields[0];
          const labelField = config.labelField ?? fields[1] ?? valueField;
          const pidField = config.pidField ?? fields[2];
          res = res.map((x: any) => {
            const p = formatStr(pidField, x);
            return {
              value: formatStr(valueField, x),
              label: formatStr(labelField, x),
              p: p === '0' ? null : p,
            };
          });
          tree = toTreeData(res, 'value', 'p');
          forEach(tree, (x) => {
            delete x.p;
          });
        }
        if (config.prepend) {
          tree = getDataArray(config.prepend).concat(tree);
        }
        if (config.append) {
          tree = tree.concat(getDataArray(config.append));
        }
        options.value = tree;
      })
      .finally(() => {
        loading.value = false;
      });
    loading.value = true;
  }

  watch(
    () => config,
    () => reload(),
    { deep: true, immediate: true },
  );

  return {
    loading,
    options,
    reload,
  };
}
